﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace MinhUtils
{
    public class StringUtil
    {
        /// <summary>
        /// The Regular Expression to match an Integer: 1, 2...
        /// </summary>
        public static string IntPattern
        {
            get
            {
                return "[0-9]+";
            }
        }

        /// <summary>
        /// The Regular Expression to match a Double: 1, 1.2...
        /// </summary>
        public static string DoublePattern
        {
            get
            {
                return "[0-9]+[\\.]?[0-9]*";
            }
        }

        public static string TextboxNewLine
        {
            get
            {
                return "\r\n";
            }
        }

        public static string TextboxTab
        {
            get
            {
                return "\t";
            }
        }

        /// <summary>
        /// The Regular Expression to match an URL: http://a.b.c.thing.com/everything/("|'). NOTE: the URL MUST end with a double or sing quote.
        /// </summary>
        public static string HttpPattern
        {
            get
            {
                return "http://[^\"']+[\"']";
                //http://regexlib.com/REDetails.aspx?regexp_id=1051
                //return @"((http\://|https\://|ftp\://)|(www.))+(([a-zA-Z0-9\.-]+\.[a-zA-Z]{2,4})|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(/[a-zA-Z0-9%:/-_\?\.'~]*)?";
            }
        }

        /// <summary>
        /// The Regular Expression to match hypertext reference: href="http://any.thing.com/everything/"
        /// </summary>
        public static string HrefPattern
        {
            get
            {
                return "href(=| = | =|= )[\"'][^\"']+[\"']";
            }
        }

        /// <summary>
        /// The Regular Expression to match image/link's source: src="http://any.thing.com/everything/"
        /// </summary>
        public static string SrcPattern
        {
            get
            {
                return "src(=| = | =|= )[\"'][^\"']+[\"']";
            }
        }

        /// <summary>
        /// The Regular Expression to match a string in quotes: "abc", 'abc'...
        /// </summary>
        public static string QuotePattern
        {
            get
            {
                return "[\"'][^\"']*[\"']";
            }
        }

        public enum DateFormat
        {
            Day_Month_Year = 0,
            Month_Day_Year = 1,
            Year_Month_Day = 2
        }

        /// <summary>
        /// Count all occurrences of s2 in s1. E.g ("a", "aabbcc") -> 2
        /// </summary>        
        public static int CountOccurences(string s1, string s2)
        {
            //http://stackoverflow.com/questions/541954/how-would-you-count-occurences-of-a-string-within-a-string-c
            return s1.Length - s1.Replace(s2, string.Empty).Length;
        }

        public static string GetStringDate(DateFormat dateFormat, string seperator)
        {
            return ToStringDate(DateTime.Today, dateFormat, seperator);
        }

        public static string GetStringDate()
        {
            return ToStringDate(DateTime.Today);
        }

        public static string GetStringDate(string seperator)
        {
            return ToStringDate(DateTime.Today, seperator);
        }

        public static string GetStringDate(DateFormat dateFormat)
        {
            return ToStringDate(DateTime.Today, dateFormat);
        }

        /// <summary>
        /// Convert DateTime data to 'd/m/y' formatted string
        /// </summary>
        /// <param name="date">The DateTime value you want to convert</param>
        public static string ToStringDate(DateTime date)
        {
            return ToStringDate(date, DateFormat.Day_Month_Year);
        }

        public static string ToStringDate(DateTime date, string seperator)
        {
            return ToStringDate(date, DateFormat.Day_Month_Year, seperator);
        }

        /// <summary>
        /// Convert DateTime data to formatted string
        /// </summary>
        /// <param name="date">The DateTime value you want to convert</param>
        public static string ToStringDate(DateTime date, DateFormat dateFormat)
        {
            return ToStringDate(date, dateFormat, "/");
        }

        public static string ToStringDate(DateTime date, DateFormat dateFormat, string seperator)
        {
            switch (dateFormat)
            {
                case DateFormat.Day_Month_Year:
                    return date.Day + seperator + date.Month + seperator + date.Year;
                case DateFormat.Month_Day_Year:
                    return date.Month + seperator + date.Day + seperator + date.Year;
                case DateFormat.Year_Month_Day:
                    return date.Year + seperator + date.Month + seperator + date.Day;
                default:
                    return String.Empty;
            }
        }

        /// <summary>
        /// Return collection of URLs (must start with http://) found in the given string
        /// </summary>
        public static ICollection<string> PickURLs(string s)
        {
            MatchCollection matchCol = Regex.Matches(s, HttpPattern);
            List<string> urlCol = new List<string>();

            foreach (Match match in matchCol)
            {
                string url = match.Value.Trim();

                if (url.EndsWith("'") || url.EndsWith("\""))
                    url = url.Remove(url.Length - 1); //remove ' or " at the end

                urlCol.Add(url);
            }

            return urlCol;
        }

        /// <summary>
        /// Return the first url found in the given string
        /// </summary>
        public static string PickURL(string s)
        {
            Match match = null;

            if (s.ToLower().IndexOf("http") >= 0)
            {
                match = Regex.Match(s, HttpPattern);

                if (match.Value != string.Empty)
                {
                    string url = match.Value.Trim(); //remove spaces at the beginning and the end
                    if (url.EndsWith("'") || url.EndsWith("\""))
                        url = url.Remove(url.Length - 1); //remove ' or " at the end

                    return url;
                }
            }

            if (s.ToLower().IndexOf("href") >= 0)
            {
                match = Regex.Match(s, HrefPattern);

                if (match.Value != string.Empty)
                {
                    match = Regex.Match(match.Value, QuotePattern);

                    if (match.Value != string.Empty)
                    {
                        StringBuilder sb = new StringBuilder(match.Value.Trim());
                        sb.Remove(sb.Length - 1, 1); //remove ' or " at the end
                        sb.Remove(0, 1); //remove ' or " at the beginning

                        return sb.ToString();
                    }
                }
            }

            if (s.ToLower().IndexOf("src") >= 0)
            {
                match = Regex.Match(s, SrcPattern);

                if (match.Value != string.Empty)
                {
                    match = Regex.Match(match.Value, QuotePattern);

                    if (match.Value != string.Empty)
                    {
                        StringBuilder sb = new StringBuilder(match.Value.Trim());
                        sb.Remove(sb.Length - 1, 1); //remove ' or " at the end
                        sb.Remove(0, 1); //remove ' or " at the beginning

                        return sb.ToString();
                    }
                }
            }

            return string.Empty;
        }
        /// <summary>
        /// Return the first POSITIVE integer found in the given string. Throw an Expcetion if none can be found.
        /// </summary>
        public static int PickInteger(string s)
        {
            //if (regexInt == null)
            //    regexInt = new Regex(IntPattern);

            Match match = Regex.Match(s, IntPattern);

            if (match.Value == string.Empty)
            {
                throw new Exception("No integer found.");
            }
            else
            {
                return Convert.ToInt32(match.Value);
            }
        }

        /// <summary>
        /// Return the first POSITIVE double found in the given string. Throw an Expcetion if none can be found.
        /// </summary>
        public static double PickDouble(string s)
        {
            Match match = Regex.Match(s, DoublePattern);

            if (match.Value == string.Empty)
            {
                throw new Exception("No double found.");
            }
            else
            {
                return Convert.ToDouble(match.Value);
            }
        }

        /// <summary>
        /// Use given Regex/Pattern to capture a value in the given string. Only return the value between the first '[' and the last ']' in the pattern. E.g if pattern is "a[^a]+c" and the found value is "abc", only "b" will be returned. If there is no '[' and/or ']' in the given pattern, an empty string will be returned.
        /// </summary>
        public static string PickValue(string s, string pattern)
        {
            try
            {
                if (pattern.Trim().Equals(string.Empty)) //empty pattern
                    return string.Empty;

                Match match = Regex.Match(s, pattern);

                if (match.Value == string.Empty) //no match
                {
                    return String.Empty;
                }
                else
                {
                    //string pattern = regex.ToString();
                    int openSquareBracketIndex = pattern.IndexOf("[");
                    int closeSquareBracketIndex = pattern.LastIndexOf("]");

                    //check if the last "]" is followed by "?", "*" or "+" or "{"
                    if (closeSquareBracketIndex < pattern.Length - 1) //not the last character
                    {
                        if (pattern[closeSquareBracketIndex + 1] == '?' || pattern[closeSquareBracketIndex + 1] == '*' || pattern[closeSquareBracketIndex + 1] == '+')
                            closeSquareBracketIndex += 1;
                        else if (pattern[closeSquareBracketIndex + 1] == '{') //{n} or {m, n} case
                        {
                            closeSquareBracketIndex = pattern.IndexOf('}', closeSquareBracketIndex + 1);
                        }
                    }

                    if(pattern.EndsWith("$"))
                        closeSquareBracketIndex += 1;

                    if (openSquareBracketIndex == -1 || closeSquareBracketIndex == -1)
                    {
                        return string.Empty;
                    }
                    else
                    {

                        StringBuilder sb = new StringBuilder(match.Value);
                        sb.Remove(0, openSquareBracketIndex); //remove all berfore "["
                        sb.Remove(sb.Length - (pattern.Length - closeSquareBracketIndex - 1), pattern.Length - closeSquareBracketIndex - 1); //remove all after "]"

                        return sb.ToString();

                    }
                }
            }
            catch
            {
                return string.Empty;
            }
        }

        /// <summary>
        /// Convert seconds into (hh:mm:ss) string
        /// </summary>
        public static string ToHHMMSS(double seconds)
        {
            if (double.IsNaN(seconds))
            {
                return "99:99:99";
            }
            else if (double.IsInfinity(seconds))
            {
                return "99:99:99";
            }
            else
            {
                //http://stackoverflow.com/questions/463642/c-what-is-the-best-way-to-convert-seconds-into-hourminutessecondsmillisecon
                TimeSpan t = TimeSpan.FromSeconds(seconds);
                return string.Format("{0:D2}:{1:D2}:{2:D2}", t.Hours, t.Minutes, t.Seconds);
            }
        }

        /// <summary>
        /// Find the last integer value in a string and increase by the given value. E.g ("abc123cde", 4) -> "abc127cde". If no integer value can be found in the given string, the original string is returned.
        /// </summary>
        public static string IncreaseLastInteger(string s, int value)
        {
            if (value == 0)
                return s;
            else
            {
                //find the last number
                int lastNumber = 0;

                try
                {
                    lastNumber = PickLastInteger(s);
                }
                catch //no number -> return s
                {
                    return s;
                }

                //get the last index of the found number
                int index = s.LastIndexOf(lastNumber.ToString());

                //cut s into 2: s1 0 -> index value, s2 index value -> end
                string s1 = s.Remove(index);
                string s2 = s.Remove(0, index);

                //s2: replace last number = last number + value
                s2 = s2.Replace(lastNumber.ToString(), (lastNumber + value).ToString());

                //return s1 + s2
                return (s1 + s2);
            }
        }

        /// <summary>
        /// Return the last POSITIVE integer found in the given string. Throw an Expcetion if none can be found.
        /// </summary>
        public static int PickLastInteger(string s)
        {
            //if (regexInt == null)
            //    regexInt = new Regex(IntPattern);

            MatchCollection mcl = Regex.Matches(s, IntPattern);

            if (mcl.Count == 0)
            {
                throw new Exception("No integer found.");
            }
            else
            {
                return Convert.ToInt32(mcl[mcl.Count - 1].Value);
            }
        }

        /// <summary>
        /// Look for all numbers in a string and pad with zeros to a specific length. E.g ("abc123", 6) -> "abc000123"
        /// </summary>
        /// <param name="input">String value to be padded</param>
        /// <param name="totalWidth">The number of characters in the resulting string, equal to the number of
        //      original characters plus any additional padding characters.</param>
        public static string PadZeros(string input, int totalWidth)
        {
            StringBuilder sb = new StringBuilder();

            //split to "non-number followed by number" strings
            MatchCollection matchCol = Regex.Matches(input, "\\D*" + DoublePattern);

            if (matchCol.Count == 0) //no number found
                return input;

            foreach (Match match in matchCol)
            {
                //retrieve numberic part
                Match matchDouble = Regex.Match(match.Value, DoublePattern);

                sb.Append(match.Value.Replace(matchDouble.Value, PadZeros(Convert.ToDouble(matchDouble.Value), totalWidth)));
            }

            Match nonNumberAtEndMatch = Regex.Match(input, "(?<=\\d)\\D+$");
            sb.Append(nonNumberAtEndMatch.Value);

            //return padded string
            return sb.ToString();
        }

        /// <summary>
        /// Padding on the left with Zeros for a specified total length
        /// </summary>
        /// <param name="value">Integer value to be padded</param>
        /// <param name="maxLength">The number of characters in the resulting string, equal to the number of
        //      original characters plus any additional padding characters.</param>
        public static string PadZeros(int value, int totalWidth)
        {
            return value.ToString().PadLeft(totalWidth, '0');
        }

        /// <summary>
        /// Padding on the left with Zeros for a specified total length
        /// </summary>
        /// <param name="value">Double value to be padded</param>
        /// <param name="totalWidth">The number of characters in the resulting string, equal to the number of
        //      original characters plus any additional padding characters.</param>
        public static string PadZeros(double value, int totalWidth)
        {
            if (value == Convert.ToInt16(value)) //round numbers 1, 2, 3...
            {
                return PadZeros(Convert.ToInt16(value), totalWidth);
            }
            else //1.23, 10.45...
            {
                //split to two path: before . and after . (1.23 -> 1 and .23)
                string[] s = value.ToString().Split('.');

                //add zeros to the first part (1 -> 001)
                s[0] = PadZeros(Convert.ToInt16(s[0]), totalWidth);

                //add second part and return (-> 001.23)
                return s[0] + "." + s[1];
            }
        }
    }
}
